
import { Action } from './Action'
import { SingleParticle } from './SingleParticle'
import { any, must } from '../core/logic'
import { Vector3 } from '../core/math/Vector3'
import { ShapeType } from "./ParticleType";
import { PositionShape, ShapeTypeEnu, VelocityShape } from "./shape";
import { PointsGeometry } from "../geom/PointsGeometry";
import { PointMaterial } from '../material/PointMaterial';
import { Mesh } from '../component/Mesh';
import { Object3D } from '../entity/Object3D';
import { Color } from '../core/math/Color';
import { Engine } from '../Engine';
import { locations } from '../gpu/locations';
import { ParticleComputer } from './ParticleComputer';
import { Base } from '../core/Base';
import { Event, EventType } from '../event/index';
import { ComponentEvent } from '../event/ComponentEvent';
import { ComponentType } from '../component/index';
import { is2DArray } from '../core/utils';

/**
 * 粒子发射器
 */
export class Emitter extends Base {
  constructor(params) {
    super()
    this.type = ComponentType.Camera;
    this.event = new ComponentEvent(this.type);
    this.renderSystem = Engine.instance._register._systemManager._systems.get("rendersystem")
    this.position = any(params.position, [0, 0, 0])
    this.positionShape = any(params.positionShape, "Cube")
    this.positionRange = any(params.positionRange, [500, 500, 0])
    this.positionRadius = any(params.positionRadius, 1)
    this.positionconeHeight = any(params.positionconeHeight, 5)
    this.velocity = any(params.velocity, [10, 10, -50])//有方向的三个，如果是圆类型的只用传第一个
    this.velocityShape = any(params.velocityShape, "Cube")
    this.velocityRange = any(params.velocityRange, [50, -20, 50])
    this.velocityRadius = any(params.velocityRadius, 2)
    this.velocityconeHeight = any(params.velocityconeHeight, 4)

    this.acceleration = any(params.acceleration, [0, 0, 0])
    this.angle = any(params.angle, 0)
    this.angleRange = any(params.angleRange, 360)
    this.angleVelocity = any(params.angleVelocity, 0)
    this.texturePath = must(params.texturePath)
    this.size = any(params.size, [[0, 1], [10, 20]])
    this.sizeRange=any(params.sizeRange, 0)
    this.color = any(params.color, [[0, 3], [[0.2, 0.2, 0.2], [0.5, 0.5, 0.5]]])
    this.opacity = any(params.opacity, [[0, 3], [0.8, 1]])
    this.particlesPerSecond = any(params.particlesPerSecond, 100)
    this.duration = any(params.duration, 3)
    this.emitterAge = any(params.emitterAge, 60)
    this.loop = any(params.loop, true)
    this.alphaCutoff = any(params.alphaCutoff, 0.5)
    this.blendingMode = any(params.blendingMode, 0.5)
    this.startTime = performance.now();
    this.particles = []
    this.alive = true
    this.age = 0
    this.createBuffer()
    this.initData()
    this.createMesh()
  }

  createBuffer() {
    // 粒子顶点buffer
    // this._particleCount = 4
    this._particleCount = this.particlesPerSecond * Math.min(this.duration, this.emitterAge)
    this._particleInstanceByteSize =
      3 * 4 + // position
      1 * 4 + // size
      3 * 4 + // color
      1 * 4 + // opacity
      1 * 4;// angle
    this.particleDataSize=9
    var particlePositionOffset = 0
    var particleSizeOffset = 3 * 4
    var particleColorOffset = 4 * 4
    var particleOpacityOffset = 7 * 4
    var particleAngleOffset = 8 * 4

    this._particlesBuffer = Engine.instance.device.createBuffer({
      label: "_particlesBuffer",
      size: this._particleCount * this._particleInstanceByteSize,
      usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
    });
    this._particlesBuffer.arrayStride = this._particleInstanceByteSize//顶点步长
    this._particlesBuffer.stepMode = 'instance'//顶点步长
    this._particlesBuffer.vertexNum = this._particleCount
    this._particlesBuffer.attributes = [
      {
        // position
        shaderLocation: locations.position[1],
        offset: particlePositionOffset,
        format: 'float32x3',
      },
      {
        // size
        shaderLocation: locations.size[1],
        offset: particleSizeOffset,
        format: 'float32',
      },
      {
        // color
        shaderLocation: locations.color[1],
        offset: particleColorOffset,
        format: 'float32x3',
      },
      {
        // opacty
        shaderLocation: locations.opacity[1],
        offset: particleOpacityOffset,
        format: 'float32',
      },
      {
        // angle
        shaderLocation: locations.rotation[1],
        offset: particleAngleOffset,
        format: 'float32',
      },
    ]
   
  }
  randomValue(min, max) {
    return min + max * (Math.random() - 0.5)
  }
  randomVector3(min, max) {
    const rand3 = new Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)
    return new Vector3().addVectors(min, new Vector3().multiplyVectors(max, rand3))
  }
  createPartice(){
    const singleParticle = new SingleParticle()
    let position, velocity, angle, size, opacity, color
    if (this.positionShape == ShapeType.Cube) {
      position = this.randomVector3(new Vector3(...this.position), new Vector3(...this.positionRange))
      singleParticle.position = position
    }
    if (this.positionShape == ShapeType.Sphere) {
      const z = 2 * Math.random() - 1
      const t = Math.PI * 2 * Math.random()
      const r = Math.sqrt(1 - z * z)
      const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z)
      position = new Vector3().addVectors(new Vector3(...this.position), vec3.multiplyScalar(this.positionRadius))
      singleParticle.position = position
    }
    if (this.positionShape == ShapeType.Circle) {
      const angle = Math.random() * 2 * Math.PI;
      const radius = Math.random() * this.positionRadius;
      position = new Vector3(
        this.position[0] + radius * Math.cos(angle),
        this.position[1] + radius * Math.sin(angle),
        this.position[2] // 圆形平面的z坐标保持不变
      );
      singleParticle.position = position
    }
    if (this.positionShape == ShapeType.Cone) {
      const height = Math.random() * this.positionconeHeight;
      const radius = (Math.random() * this.positionRadius) * (1 - height / this.positionconeHeight);
      const angle = Math.random() * 2 * Math.PI;

      const x = radius * Math.cos(angle);
      const y = height;
      const z = radius * Math.sin(angle);

      position = new Vector3(x, y, z).add(new Vector3(...this.position));
      singleParticle.position = position
    }
    if (this.positionShape == ShapeType.Hemisphere) {
      const z = Math.sqrt(1 - (Math.random() * 2 - 1) ** 2); // 限制在半球范围内
      const t = Math.PI * 2 * Math.random();
      const r = Math.sqrt(1 - z * z);
      const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z);
      position = new Vector3().addVectors(new Vector3(...this.position), vec3.multiplyScalar(this.positionRadius));
      singleParticle.position = position
    }
    if (this.velocityShape == ShapeType.Cube) {
      velocity = this.randomVector3(new Vector3(...this.velocity), new Vector3(...this.velocityRange))
      singleParticle.velocity = velocity
    }
    if (this.velocityShape == ShapeType.Sphere) {
      const direction = new Vector3().subVectors(position, new Vector3(...this.position))
      const speed = this.randomValue(this.velocity[0], this.velocityRadius)
      velocity = direction.normalize().multiplyScalar(speed)
      singleParticle.velocity = velocity
    }
    if (this.velocityShape == ShapeType.Circle) {
      const angle = Math.random() * 2 * Math.PI;
      const speed = this.randomValue(this.velocity[0], this.velocityRadius);
      velocity = new Vector3(
        speed * Math.cos(angle),
        speed * Math.sin(angle),
        0 // 圆形平面的z分量速度设为0
      );
      singleParticle.velocity = velocity
    }
    if (this.velocityShape == ShapeType.Cone) {
      const heightRatio = Math.random();
      const baseRadius = this.velocityRadius * Math.sqrt(heightRatio);
      const angle = Math.random() * 2 * Math.PI;
      const speed = this.randomValue(this.velocity[0], this.velocityRadius);
      const x = speed * baseRadius * Math.cos(angle);
      const y = speed * heightRatio * this.velocityconeHeight;
      const z = speed * baseRadius * Math.sin(angle);

      velocity = new Vector3(x, y, z);
      singleParticle.velocity = velocity
    }
    if (this.velocityShape == ShapeType.Hemisphere) {
      const phi = Math.acos(1 - 2 * Math.random()); // 高度角，限制在半球内
      const theta = Math.random() * 2 * Math.PI; // 方位角
      const speed = this.randomValue(this.velocity[0], this.velocityRadius);

      const x = speed * Math.sin(phi) * Math.cos(theta);
      const y = speed * Math.sin(phi) * Math.sin(theta);
      const z = speed * Math.cos(phi);
      velocity = new Vector3(x, y, z);
      singleParticle.velocity = velocity
    }
    angle = this.randomValue(this.angle, this.angleRange)
    singleParticle.angle = angle
    singleParticle.acceleration = new Vector3(...this.acceleration) 
    if(is2DArray(this.size)){
      size = this.size[1][0]
    }else{
      size= this.randomValue(this.size, this.sizeRange)
      size=Math.abs(size)
    }
    if(is2DArray(this.color)){
      color = this.color[1][0]
    }else{
      color=this.color
    }
    if(is2DArray(this.opacity)){
      opacity = this.opacity[1][0]
    }else{
      opacity=this.opacity
    }

    singleParticle.color = this.color
    singleParticle._color = color
    singleParticle.size = this.size
    singleParticle._size = size 
    singleParticle.opacity = this.opacity
    singleParticle._opacity = opacity
    return singleParticle
  }
  initData() {
    let particleData = []
    for (let index = 0; index < this._particleCount; index++) {
      let position, velocity, angle, size, opacity, color
      const singleParticle = new SingleParticle()
      if (this.positionShape == ShapeType.Cube) {
        position = this.randomVector3(new Vector3(...this.position), new Vector3(...this.positionRange))
        singleParticle.position = position
      }
      if (this.positionShape == ShapeType.Sphere) {
        const z = 2 * Math.random() - 1
        const t = Math.PI * 2 * Math.random()
        const r = Math.sqrt(1 - z * z)
        const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z)
        position = new Vector3().addVectors(new Vector3(...this.position), vec3.multiplyScalar(this.positionRadius))
        singleParticle.position = position
      }
      if (this.positionShape == ShapeType.Circle) {
        const angle = Math.random() * 2 * Math.PI;
        const radius = Math.random() * this.positionRadius;
        position = new Vector3(
          this.position[0] + radius * Math.cos(angle),
          this.position[1] + radius * Math.sin(angle),
          this.position[2] // 圆形平面的z坐标保持不变
        );
        singleParticle.position = position
      }
      if (this.positionShape == ShapeType.Cone) {
        const height = Math.random() * this.positionconeHeight;
        const radius = (Math.random() * this.positionRadius) * (1 - height / this.positionconeHeight);
        const angle = Math.random() * 2 * Math.PI;

        const x = radius * Math.cos(angle);
        const y = height;
        const z = radius * Math.sin(angle);

        position = new Vector3(x, y, z).add(new Vector3(...this.position));
        singleParticle.position = position
      }
      if (this.positionShape == ShapeType.Hemisphere) {
        const z = Math.sqrt(1 - (Math.random() * 2 - 1) ** 2); // 限制在半球范围内
        const t = Math.PI * 2 * Math.random();
        const r = Math.sqrt(1 - z * z);
        const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z);
        position = new Vector3().addVectors(new Vector3(...this.position), vec3.multiplyScalar(this.positionRadius));
        singleParticle.position = position
      }
      if (this.velocityShape == ShapeType.Cube) {
        velocity = this.randomVector3(new Vector3(...this.velocity), new Vector3(...this.velocityRange))
        singleParticle.velocity = velocity
      }
      if (this.velocityShape == ShapeType.Sphere) {
        const direction = new Vector3().subVectors(position, new Vector3(...this.position))
        const speed = this.randomValue(this.velocity[0], this.velocityRadius)
        velocity = direction.normalize().multiplyScalar(speed)
        singleParticle.velocity = velocity
      }
      if (this.velocityShape == ShapeType.Circle) {
        const angle = Math.random() * 2 * Math.PI;
        const speed = this.randomValue(this.velocity[0], this.velocityRadius);
        velocity = new Vector3(
          speed * Math.cos(angle),
          speed * Math.sin(angle),
          0 // 圆形平面的z分量速度设为0
        );
        singleParticle.velocity = velocity
      }
      if (this.velocityShape == ShapeType.Cone) {
        const heightRatio = Math.random();
        const baseRadius = this.velocityRadius * Math.sqrt(heightRatio);
        const angle = Math.random() * 2 * Math.PI;
        const speed = this.randomValue(this.velocity[0], this.velocityRadius);
        const x = speed * baseRadius * Math.cos(angle);
        const y = speed * heightRatio * this.velocityconeHeight;
        const z = speed * baseRadius * Math.sin(angle);

        velocity = new Vector3(x, y, z);
        singleParticle.velocity = velocity
      }
      if (this.velocityShape == ShapeType.Hemisphere) {
        const phi = Math.acos(1 - 2 * Math.random()); // 高度角，限制在半球内
        const theta = Math.random() * 2 * Math.PI; // 方位角
        const speed = this.randomValue(this.velocity[0], this.velocityRadius);

        const x = speed * Math.sin(phi) * Math.cos(theta);
        const y = speed * Math.sin(phi) * Math.sin(theta);
        const z = speed * Math.cos(phi);
        velocity = new Vector3(x, y, z);
        singleParticle.velocity = velocity
      }
      angle = this.randomValue(this.angle, this.angleRange)
      singleParticle.angle = angle
      singleParticle.acceleration = new Vector3(...this.acceleration) 


      if(is2DArray(this.size)){
        size = this.size[1][0]
      }else{
        size= this.randomValue(this.size, this.sizeRange)
        size=Math.abs(size) 
      }
      if(is2DArray(this.color)){
        color = this.color[1][0]
      }else{
        color=this.color
      }
      if(is2DArray(this.opacity)){
        opacity = this.opacity[1][0]
      }else{
        opacity=this.opacity
      }
      singleParticle.color = this.color
      singleParticle._color = color
      singleParticle.size = this.size
      singleParticle._size =size
      singleParticle.opacity = this.opacity
      singleParticle._opacity = opacity
      particleData.push(position.x, position.y, position.z, size, ...color, 0, angle)
      this.particles[index] = singleParticle
    }
    this.pdata = new Float32Array(particleData)
    Engine.instance.device.queue.writeBuffer(this._particlesBuffer,
      0,
      this.pdata
    );

  }


  createMesh() {
    let blend = {
      alphaSrcFactor: "one",
      alphaDstFactor: "zero",
      alphaOperation: "add",
      colorSrcFactor: "one",
      colorDstFactor: "zero",
      colorOperation: "add",
    };
    if (typeof (this.blendingMode) == "string") {
      switch (this.blendingMode) {
        case "NormalBlending":
          blend = {
            alphaSrcFactor: "one",
            alphaDstFactor: "one-minus-src-alpha",
            alphaOperation: "add",
            colorSrcFactor: "src-alpha",
            colorDstFactor: "one-minus-src-alpha",
            colorOperation: "add",
          };
          break;
        case "AdditiveBlending":
          blend = {
            alphaSrcFactor: "one",
            alphaDstFactor: "one",
            alphaOperation: "add",
            colorSrcFactor: "src-alpha",
            colorDstFactor: "one",
            colorOperation: "add",
          };
          break;
        case "SubtractiveBlending":
          blend = {
            alphaSrcFactor: "zero",
            alphaDstFactor: "one",
            alphaOperation: "add",
            colorSrcFactor: "zero",
            colorDstFactor: "one-minus-src",
            colorOperation: "add",
          };
          break;
        case "MultiplyBlending":
          blend = {
            alphaSrcFactor: "zero",
            alphaDstFactor: "src-alpha",
            alphaOperation: "add",
            colorSrcFactor: "zero",
            colorDstFactor: "src",
            colorOperation: "add",
          };
          break;
        default:
          break;
      }
    } else {
      blend = this.blendingMode
    }
   const sample={
    addressModeU: "clamp-to-edge",
    addressModeV: "clamp-to-edge",
    addressModeW: "clamp-to-edge",
    magFilter: "linear",
    minFilter: "linear",
    mipmapFilter: "linear"
}
    this._geometry = new PointsGeometry({ buffer: this._particlesBuffer, })
    this._material = new PointMaterial({
      map:{
        sampler: sample,
        url: this.texturePath,
      } ,
      alphaCutoff:this.alphaCutoff,
      alphaSrcFactor: blend?.alphaSrcFactor,
      alphaDstFactor: blend?.alphaDstFactor,
      alphaOperation: blend?.alphaOperation,
      colorSrcFactor: blend?.colorSrcFactor,
      colorDstFactor: blend?.colorDstFactor,
      colorOperation: blend?.colorOperation,
    })
    this._mesh = new Mesh({ geometry: this._geometry, material: this._material })
    this._object3D = new Object3D({ position: [0, 0, 0], rotation: [0, 0, 0] });
    this._object3D.addComponent(this._mesh);
    Engine.instance.scene.addEntity(this._object3D)
  }
  async update(dt) {

    const recycleIndices = []
    for (let i = 0; i < this._particleCount; i++) {
      const particle = this.particles[i]
      if (particle.alive) {
        particle.update(dt)
        if (particle.age > this.duration) {
          this.particles[i] = this.createPartice()
         this.particles[i].alive = 1.0
        }
        const index = i * this.particleDataSize; // 假设每个粒子在 pdata 中占用的大小  
        this.pdata[index] = particle.position.x;        // x 坐标  
        this.pdata[index + 1] = particle.position.y;    // y 坐标  
        this.pdata[index + 2] = particle.position.z;    // z 坐标  
        this.pdata[index + 3] = particle._size;          // 尺寸  
        this.pdata[index + 4] = particle._color[0];       // 红色通道  
        this.pdata[index + 5] = particle._color[1];       // 绿色通道  
        this.pdata[index + 6] = particle._color[2];       // 蓝色通道  
        this.pdata[index + 7] = particle._opacity;       // 不透明度  
        this.pdata[index + 8] = particle.angle;         // 角度 
      }
    }


    if (!this.alive) return

    if (this.age < this.duration) {
      let startIndex = Math.round(this.particlesPerSecond * (this.age + 0))
      let endIndex = Math.round(this.particlesPerSecond * (this.age + dt))
      if (endIndex > this._particleCount) {
        endIndex = this._particleCount
      }
      for (let i = startIndex; i < endIndex; i++) {
        this.particles[i].alive = 1.0
      }
    }

    // for (let j = 0; j < recycleIndices.length; j++) {
    //   let i = recycleIndices[j]
    //   this.particles[i] = this.createPartice()
    //   this.particles[i].alive = 1.0
    //   const index = i * this.particleDataSize;
    //   this.pdata[index] = this.particles[i].position.x
    //   this.pdata[index + 1] = this.particles[i].position.y
    //   this.pdata[index + 2] = this.particles[i].position.z
    //   this.pdata[index + 3] = this.particles[i]._size;          // 尺寸  
    //   this.pdata[index + 4] = this.particles[i]._color[0];       // 红色通道  
    //   this.pdata[index + 5] = this.particles[i]._color[1];       // 绿色通道  
    //   this.pdata[index + 6] = this.particles[i]._color[2];       // 蓝色通道  
    //   this.pdata[index + 7] = this.particles[i]._opacity;       // 不透明度  
    //   this.pdata[index + 8] = this.particles[i].angle;         // 角度 
    // }
   
    Engine.instance.device.queue.writeBuffer(this._particlesBuffer,
      0,
      this.pdata
    );
    this.event.mode = "value";
    this.publish(this.event);
    this.age += dt

    // if (this.age > this.emitterAge && !this.loop) {
    //   this.alive = false
    // }
  }
}
